package ui.panels.commands;

import gameEngine.agents.choosingStrategy.representation.StrategyRepresentation;
import gameEngine.agents.distrebution.representation.DistributionRepresentation;
import gameEngine.agents.distrebution.representation.ExponentialRepresentation;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import Data.ConfigurationParameters;
import Data.ConfigurationParameters.Distribution;

import ui.flowManager.FlowManagerImpl;
import ui.flowManager.expirimentProperties.AdditionalSettings;
import ui.flowManager.expirimentProperties.AgentRepresentation;
import ui.flowManager.expirimentProperties.ExperimentProperties;
import ui.flowManager.expirimentProperties.ExperimentProperties.Agent_Group;


/**
 * 
 * @author vladimir && alon
 *
 *  The command open XML file of the experiment
 */
public class OpenXMLExperimentCommand implements Command{

	private File	 				file = null;
	private ExperimentProperties 	props = null;
	Logger log = Logger.getLogger(this.getClass());

	public void execute()
	{
		//case file  not updated
		if(file == null)
		{
			return;
		}
		//take the experiment properties
		props = FlowManagerImpl.getInstance().getProps();
		
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder;
		Document doc = null;
		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
			doc = docBuilder.parse (file);
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// normalize text representation
		doc.getDocumentElement ().normalize();
		log.debug("Root element of the doc is " + doc.getDocumentElement().getNodeName());

		//the name  of the experiment
		String name = getTagValue("ExperimentName", doc.getDocumentElement());
		props.setExperimentName(name);
		
		NodeList listOfGames = doc.getElementsByTagName("Game");
		int numberOfGames = listOfGames.getLength();
		log.debug("Total number of games : " + numberOfGames);

		props.setGames(numberOfGames);
		
		//set the games
		for(int i = 0; i < numberOfGames; ++i)
		{
			//set game, the + 1 because games start from index 1
			setGame(doc, (Element)listOfGames.item(i), i+1);
		}
	}
	
	
	public void setFile(File file) {
		this.file = file;
	}
	
	
	private void setGame(Document doc, Element gameElement, int gameNumber)
	{
		NodeList gameChilds=gameElement.getChildNodes();
		int 		numTurns = 0;
		int 		numRepeats = 0;
		String 		observation = null;
		int 		numStudiedPlayers = 0;
		int 		numControlPlayers = 0;
		NodeList 	listOfControlPlayers = null;		
		NodeList 	listOfstudiedPlayers = null;
		
		for(int i = 0; i < gameChilds.getLength(); ++i){
			
			//the name of the tag
			String name = gameChilds.item(i).getNodeName();
			
			//case junk
			if(name.equals("#text")){
				//go to next child of game
				continue;				
			}
			//case observation
			else if(name.equals(ConfigurationParameters.Observation.OBSERVATION))
			{
				observation = getTagValue(name, gameElement);
			}
			//case number of turns
			else if(name.equals("NunberOfTurns"))
			{
				String temp = getTagValue(name, gameElement);
				numTurns = Integer.parseInt(temp);
			}
			else if(name.equals("NunberOfRepeats"))
			{
				String temp = getTagValue(name, gameElement);
				numRepeats = Integer.parseInt(temp);
			}
			else if(name.equals("StudiedPlayers"))
			{
				NodeList list = gameElement.getElementsByTagName("StudiedPlayers");
				Element studiedPlayers = (Element)list.item(0);
				listOfstudiedPlayers = studiedPlayers.getElementsByTagName("Player");
				numStudiedPlayers = listOfstudiedPlayers.getLength();
			}
			else if(name.equals("ControlPlayers"))
			{
				NodeList list  = gameElement.getElementsByTagName("ControlPlayers");
				Element controlPlayers = (Element)list.item(0);
				listOfControlPlayers = controlPlayers.getElementsByTagName("Player");	
				numControlPlayers = listOfControlPlayers.getLength();
			}
		}
		//end for
		
		props.setGameData(gameNumber, numTurns, numRepeats, observation);
		//create the players
		props.setPlayers(gameNumber, numStudiedPlayers, Agent_Group.STUDIED_AGENT);
		props.setPlayers(gameNumber, numControlPlayers, Agent_Group.CONTROL_AGENT);	
		
		//set the studied players
		for(int i = 0; i < numStudiedPlayers; ++i)
		{
			Element playerElement = (Element)listOfstudiedPlayers.item(i);
			setPlayer(doc, playerElement, gameNumber, i+1, Agent_Group.STUDIED_AGENT);
		}	
		//set the control players
		for(int i = 0; i < numControlPlayers; ++i)
		{
			Element playerElement = (Element)listOfControlPlayers.item(i);
			setPlayer(doc, playerElement, gameNumber, i+1, Agent_Group.CONTROL_AGENT);
		}
	}
	
	
	/**
	 * Set the player properties from XML
	 * @param doc
	 * @param playerElement
	 * @param gameNumber
	 * @param playerNumber
	 */
	private void setPlayer(Document doc, Element playerElement,int gameNumber,
						int playerNumber, Agent_Group group)
	{
		//take the agent from the game
		AgentRepresentation agRepresentation = props.getPlayer(gameNumber, playerNumber,group);
		String temp = null;
		
		//set initialized
		agRepresentation.setInitialized(true);
		//ID
		temp = getTagValue("ID", playerElement);
		int id = Integer.parseInt(temp);
		agRepresentation.setID(id);
		
		//set Teammate model
		String teammate = getTagValue(ConfigurationParameters.TeammateModel.TEAMMATE_MODEL,
								playerElement);
		agRepresentation.setTeammateModelType(teammate);
		
		//set Analyzer 
		String analyzer = getTagValue(ConfigurationParameters.Analyzer.ANALYZER,
							playerElement);
		agRepresentation.setAnalyzerType(analyzer);
		
		//set Epsilon
		temp = getTagValue("Epsilon",
				playerElement);
		double epsilon = Double.parseDouble(temp);
		agRepresentation.setEpsilon(epsilon);
		
		//set Competence
		temp = getTagValue("Competence", playerElement);
		double competence = Double.parseDouble(temp);
		agRepresentation.setCompetence(competence);
	
		//set Additional settings, first read from the XML 
		AdditionalSettings additionalSettings = readAdditionalSetings
									( doc, playerElement, agRepresentation);
		agRepresentation.setAdditionalSettings(additionalSettings);
		
		//set Distribution
		NodeList list = playerElement.getElementsByTagName(ConfigurationParameters.Distribution.DISTRIBUTION);
		Element distElement = (Element) list.item(0);
		setDistribution(doc, distElement,agRepresentation);
	
		//set Strategy
		list = playerElement.getElementsByTagName(ConfigurationParameters.Strategy.STRATEGY);
		Element strategyElement = (Element) list.item(0);
		//take the strategy object
		StrategyRepresentation strategyRepresentation = setStrategy(doc, strategyElement);
		agRepresentation.setStrategyType(strategyRepresentation);
	}
	
	/**
	 * Set distribution data from XML
	 * @param doc
	 * @param distElement
	 * @param agRepresentation
	 */
	private void setDistribution(Document doc, Element distElement, AgentRepresentation agRepresentation)
	{
		//get the text value
		String distributionName = getTagValue("Name", distElement);
		DistributionRepresentation.openXMLElement(doc, distElement, agRepresentation, distributionName);
	}

	/**
	 * 
	 * @param doc
	 * @param strategyElement
	 * @return strategyRepresentation
	 */
	private StrategyRepresentation setStrategy(Document doc, Element strategyElement)
	{
		//get the text value
		StrategyRepresentation strategyRepresentation = StrategyRepresentation.openXMLElement(doc, strategyElement);
		return strategyRepresentation;
	}

	
	private AdditionalSettings readAdditionalSetings(Document doc,
			Element playerElement, AgentRepresentation agentRepresentation)
	{
		AdditionalSettings additionalSettings = null;
		
		NodeList listOfPlayers = playerElement.getElementsByTagName("AdditionalSettings");
		//there just one element additionalSettings 
		Element aditionalsettingsElement = (Element)listOfPlayers.item(0);
		
		String haveStr = getTagValue("Have", aditionalsettingsElement);
		
		//case have additional settings
		if(haveStr.equals("True"))
		{
			//create the AdditionalSettings to contain the data
			additionalSettings = new AdditionalSettings();
			//set the log boolean
			String logStr = getTagValue("Log", aditionalsettingsElement);
			boolean logFlag;
			if(logStr.equals("True"))
				logFlag = true;
			else
				logFlag = false;
			additionalSettings.setLog(logFlag);
			
			//set the toStop boolean
			String toStopStr = getTagValue("ToStop", aditionalsettingsElement);
			boolean toStopFlag;
			if(toStopStr.equals("True"))
				toStopFlag = true;
			else
				toStopFlag = false;
			additionalSettings.setStopAutomatically(toStopFlag);
		}
		
		return additionalSettings;
	}
	
	/**
	 * @param sTag tag name
	 * @param eElement
	 * @return return the tag value.
	 */
	private String getTagValue(String sTag, Element eElement) {
		NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes();
	 
	        Node nValue = (Node) nlList.item(0);
	 
		return nValue.getNodeValue();
	 }	
	
	
}
